package logic

import (
	"context"
	"fmt"
	"github.com/golang-jwt/jwt/v5"
	"github.com/jinzhu/copier"
	"github.com/zeromicro/go-zero/core/logx"
	"grpc-common/ucenter/types/login"
	kmodel "listenboos-common/kafka/model"
	"listenboos-common/tools"
	"strconv"
	"time"
	"ucenter/domain"
	"ucenter/internal/svc"
	"ucenter/kafka/client"
	"ucenter/model"
)

type LoginByPhoneLogic struct {
	ctx                 context.Context
	svcCtx              *svc.ServiceContext
	userInfoDomain      *domain.UserInfoDomain
	vipConfigDomain     *domain.VipConfigDomain
	userPaidTrackDomain *domain.UserPaidTrackDomain
	userPaidAlbumDomain *domain.UserPaidAlbumDomain
	colDomain           *domain.CollectDomain
	accountDomain       *domain.UserAccountDomain
	logx.Logger
}

func NewLoginByPhoneLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginByPhoneLogic {
	return &LoginByPhoneLogic{
		ctx:                 ctx,
		svcCtx:              svcCtx,
		userInfoDomain:      domain.NewUserInfoDomain(svcCtx.DB),
		vipConfigDomain:     domain.NewVipConfigDomain(svcCtx.DB),
		userPaidTrackDomain: domain.NewUserPaidTrackDomain(svcCtx.DB),
		userPaidAlbumDomain: domain.NewUserPaidAlbumDomain(svcCtx.DB),
		colDomain:           domain.NewCollectDomain(svcCtx.MongoClient, svcCtx.DB),
		accountDomain:       domain.NewUserAccountDomain(svcCtx.DB),
		Logger:              logx.WithContext(ctx),
	}
}
func (l *LoginByPhoneLogic) WechatLogin(in *login.WechatReq) (*login.WechatRep, error) {
	// todo: add your logic here and delete this line
	// GET https://api.weixin.qq.com/sns/jscode2session?
	//appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
	// https://api.weixin.qq.com/sns/jscode2session?
	//appid=wx763ba16269aa94c1&secret=37380d30150d2545888377d87a3fe43c&js_code=&grant_type=authorization_cod
	// 获取用户openId
	logx.Info("ucenter roc login")
	appId := l.svcCtx.Config.Wechat.AppId
	appSecret := l.svcCtx.Config.Wechat.AppSecret
	grantType := "authorization_cod"
	path := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=%s", appId, appSecret, in.Code, grantType)
	wxres, err := tools.Get(path)
	if err != nil {
		logx.Error("请求微信登录接口失败")
		return nil, err
	}
	// 从数据库查询用户信息是否存在
	ctx := context.Background()
	user, err := l.userInfoDomain.FindByOpenId(ctx, wxres.Openid)
	if user == nil && err != nil {
		// 如果认证失败，不存在 往数据库里插入
		user, err = l.userInfoDomain.Save(ctx, wxres.Openid)
		if err != nil {
			return nil, err
		}
	}
	// 存在，则往redis里面存储用户信息
	//marshal, err := json.Marshal(&user)
	//if err != nil {
	//	return nil, err
	//}
	key := l.svcCtx.Config.JWT.AccessSecret
	expire := l.svcCtx.Config.JWT.AccessExpire
	token, err := l.getJwtToken(key, time.Now().Unix(), expire, user.Id)
	l.svcCtx.Cache.SetCtx(ctx, tools.LoginKey+token, user)
	// 先用jwt一个token，不包含refreshToken
	return &login.WechatRep{
		Token:        token,
		RefreshToken: token,
	}, nil
}
func (l *LoginByPhoneLogic) GetUserInfo(in *login.UserInfoReq) (*login.UserInfoRes, error) {
	// 根据id查询数据库
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	key := tools.LoginKey + in.Token
	user := model.NewUserInfo()
	var u login.UserInfoRes
	err := l.svcCtx.Cache.GetCtx(ctx, key, user)
	if err != nil {
		return nil, err
	}
	if user.Id == 0 {
		id, err := tools.ParseToken(in.Token, l.svcCtx.Config.JWT.AccessSecret)
		if err != nil {
			return nil, err
		}
		user, err = l.userInfoDomain.FindById(ctx, id)
		if err != nil {
			logx.Errorf("查找用户信息失败 error: %v", err)
			return nil, err
		}
	}
	err = copier.Copy(&u, &user)
	return &u, err
}

func (l *LoginByPhoneLogic) LoginByPhone(in *login.RegReq) (*login.RegRes, error) {
	// todo: add your logic here and delete this line

	return &login.RegRes{}, nil
}
func (l *LoginByPhoneLogic) getJwtToken(secretKey string, iat, seconds, userId int64) (string, error) {
	claims := make(jwt.MapClaims)
	claims["exp"] = iat + seconds
	claims["iat"] = iat
	claims["userId"] = userId
	token := jwt.New(jwt.SigningMethodHS256)
	token.Claims = claims
	return token.SignedString([]byte(secretKey))
}

func (l *LoginByPhoneLogic) UpdateUser(in *login.UpdateUserReq) (*login.UpdateUserRes, error) {
	err := l.userInfoDomain.UpdateUser(in)
	if err != nil {
		return nil, err
	}
	return &login.UpdateUserRes{}, nil
}

func (l *LoginByPhoneLogic) GetUserInfoByUserId(in *login.UserInfoIdReq) (*login.UserInfoIdResp, error) {
	var u login.UserInfoIdResp
	user, err := l.userInfoDomain.FindById(l.ctx, in.UserId)
	if err != nil {
		logx.Errorf("查找用户信息失败 error: %v", err)
		return nil, err

	}
	err = copier.Copy(&u, &user)
	return &u, err
}

func (l *LoginByPhoneLogic) FindAllServiceConfig(in *login.FindAllServiceConfigReq) (*login.FindAllServiceConfigResp, error) {
	configs, err := l.vipConfigDomain.FindAll(l.ctx)
	if err != nil {
		return nil, err
	}
	vips := []*login.VipServiceConfig{}
	err = copier.Copy(&vips, &configs)
	if err != nil {
		return nil, err
	}
	return &login.FindAllServiceConfigResp{VipServiceConfig: vips}, nil
}

func (l *LoginByPhoneLogic) FindUserPayAlbum(in *login.FindUserPayAlbumReq) (*login.FindUserPayAlbumResp, error) {
	// 查找用户购买的专辑
	album, err := l.userPaidAlbumDomain.FindPaidAlbum(l.ctx, in.UserId, in.AlbumId)
	if err != nil {
		return nil, err
	}
	if album.Id != 0 {
		return &login.FindUserPayAlbumResp{AlbumId: album.AlbumId}, nil
	}
	// 根据albumId查找购买过的音频
	tracks, err := l.userPaidTrackDomain.FindPaidTrack(l.ctx, in.AlbumId)
	if err != nil {
		return nil, err
	}
	var ids []int64
	for _, track := range tracks {
		ids = append(ids, track.TrackId)
	}

	return &login.FindUserPayAlbumResp{TrackId: ids}, nil
}

func (l *LoginByPhoneLogic) CollectTrack(in *login.CollectTrackReq) (*login.CollectTrackResp, error) {
	// 查找用户是否已经收藏
	collect := &model.Collect{}
	var err error
	collect, err = l.colDomain.FindCollectTo1(l.ctx, in.UserId, in.TrackId)
	if err != nil {
		return nil, err
	}
	var trackKafka kmodel.TrackModel
	trackKafka.TrackId = in.TrackId
	trackKafka.UserId = in.UserId
	var isDeleted int64
	//先王mongo里插入一条
	if collect != nil {
		// 更新
		isDeleted, err = l.colDomain.Update(l.ctx, in.UserId, in.TrackId)
		if err != nil {
			return nil, err
		}
		isDeleted, err = l.colDomain.UpdateMysql(l.ctx, in.UserId, in.TrackId)
		if err != nil {
			return nil, err
		}
		// kafka异步调用存数量
		trackKafka.IsDeleted = isDeleted
		err = client.PushV1(&trackKafka)
		if err != nil {
			return nil, err
		}
	} else {
		// 不存在
		isDeleted, err = l.colDomain.Create(l.ctx, in.UserId, in.TrackId)
		if err != nil {
			return nil, err
		}
		// 再插入mysql
		isDeleted, err = l.colDomain.CreateMysql(l.ctx, in.UserId, in.TrackId)
		if err != nil {
			return nil, err
		}
		// kafka异步调用存数量
		trackKafka.IsDeleted = isDeleted
		err = client.PushV1(&trackKafka)
		if err != nil {
			return nil, err
		}
	}
	return &login.CollectTrackResp{
		IsDeleted: isDeleted,
	}, nil
}

func (l *LoginByPhoneLogic) IsCollect(in *login.CollectTrackReq) (*login.IsCollectTrackResp, error) {
	// 查找mongo是否有
	var isCol int64
	to1, err := l.colDomain.FindCollectTo1(l.ctx, in.UserId, in.TrackId)
	if err != nil {
		return nil, err
	}
	if to1 == nil {
		isCol, err = l.colDomain.FindCollectMysql(l.ctx, in.UserId, in.TrackId)
		return &login.IsCollectTrackResp{IsCollect: isCol}, err
	}
	return &login.IsCollectTrackResp{IsCollect: isCol}, err
}

func (l *LoginByPhoneLogic) GetUserPaIdTrack(in *login.GetUserPaIdTrackReq) (*login.GetUserPaIdTrackResp, error) {
	album, err := l.userPaidAlbumDomain.FindPaidAlbum(l.ctx, in.UserId, in.AlbumId)
	if err != nil {
		return nil, err
	}
	var ts []*login.TrackSetting
	if album.AlbumId == 0 {
		tracks, err := l.userPaidTrackDomain.FindPaidTrackByUserId(l.ctx, in.UserId, in.TrackId)
		if err != nil {
			return nil, err
		}
		for _, track := range tracks {
			t := login.TrackSetting{}
			t.TrackId = track.TrackId
			ts = append(ts, &t)
		}
	} else {
		return &login.GetUserPaIdTrackResp{AlbumId: album.AlbumId}, nil
	}
	return &login.GetUserPaIdTrackResp{TrackSettingInterface: ts}, nil
}

func (l *LoginByPhoneLogic) GetAccountBalance(in *login.GetAccountBalanceReq) (*login.GetAccountBalanceResp, error) {
	// 查找用户余额
	account, err := l.accountDomain.FindUserAccount(l.ctx, in.UserId)
	if err != nil {
		return nil, err
	}
	availableAccount, err := strconv.ParseFloat(account.AvailableAmount, 64)
	return &login.GetAccountBalanceResp{AccountBalance: float32(availableAccount)}, nil
}
